package com.hanserwei.han_picture.manager.uploadtemplate;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.hanserwei.han_picture.config.TencentCosClient;
import com.hanserwei.han_picture.domain.exception.ApiException;
import com.hanserwei.han_picture.domain.file.UploadPictureResult;
import com.hanserwei.han_picture.enums.ResponseCodeEnum;
import com.hanserwei.han_picture.manager.CosManager;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.Date;

/**
 * 模版方法模式兼容本地文件上传和 URL上传
 */
@Slf4j
public abstract class PictureUploadTemplate {
    @Resource
    private TencentCosClient tencentCosClient;
    @Resource
    private CosManager cosManager;

    /**
     * 文件上传
     *
     * @param t                本地文件 / URL
     * @param uploadPathPrefix 上传路径前缀
     * @return 封装图片基本信息的对象
     */
    public <T> UploadPictureResult uploadPicture(T t, String uploadPathPrefix) {
        // 1. 校验图片(抽象方法, 由子类实现)
        validPicture(t);
        // 2. 拼接文件名称
        String date = DateUtil.formatDate(new Date());
        String uuid = RandomUtil.randomString(10);
        // 获取原始文件名, 拼接文件名
        String originalFilename = getOriginalFilename(t);
        String suffix = FileUtil.getSuffix(originalFilename);
        String fileName = String.format("%s_%s.%s", date, uuid, (StrUtil.equals(suffix, "") ? "png" : suffix));
        // 3. 划分文件夹
        String uploadPath = String.format("/%s/%s", uploadPathPrefix, fileName);
        // 4. 上传图片到 COS
        File file = null;
        try {
            // 1. 创建临时文件
            String tempPrefix = "upload_" + RandomUtil.randomString(6);
            file = File.createTempFile(tempPrefix, null);
            // 处理文件, 本地文件: 上传到服务器, URL: 下载文件
            processFile(t, file);
            // 2. 上传图片到 COS
            PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);
            // 3. 使用数据万象处理图片
            ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 4. 封装返回结果
            return buildResult(originalFilename, imageInfo, uploadPath, file);
        } catch (Exception e) {
            log.error("file upload fail", e);
            throw new ApiException(ResponseCodeEnum.SYSTEM_ERROR, "上传文件失败！");
        } finally {
            // 5. 删除临时文件
            deleteTempFile(file);
        }
    }

    /**
     * 封装图片信息
     *
     * @param originalFilename 文件名
     * @param imageInfo        图片处理
     * @param uploadPath       上传路径
     * @param file             文件
     * @return UploadPictureResult
     */
    private UploadPictureResult buildResult(String originalFilename, ImageInfo imageInfo, String uploadPath, File file) {
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        // 计算宽高比
        double scale = NumberUtil.round(width * 1.0 / height, 2).doubleValue();
        // 4. 封装图片信息
        UploadPictureResult result = new UploadPictureResult();
        result.setUrl(tencentCosClient.getHost() + uploadPath);
        result.setPicName(FileUtil.mainName(originalFilename));
        result.setPicSize(FileUtil.size(file));
        result.setPicWidth(width);
        result.setPicHeight(height);
        result.setPicScale(scale);
        result.setPicFormat(imageInfo.getFormat());
        // 获取图片主色调
        result.setPicColor(imageInfo.getAve());
        return result;
    }


    /**
     * 校验图片 / 校验 URL
     *
     * @param t   图片 / URL
     * @param <T> 图片 / URL
     */
    protected abstract <T> void validPicture(T t);

    /**
     * 获取原始文件名, 拼接文件名
     *
     * @param t   源文件: 本地文件 / URL
     * @param <T> 源文件类型
     * @return 文件名
     */
    protected abstract <T> String getOriginalFilename(T t);

    /**
     * 处理文件, 本地文件: 上传到服务器, URL: 下载文件
     *
     * @param t   本地文件 /  URL
     * @param <T> 文件上传方式
     */
    protected abstract <T> void processFile(T t, File file) throws IOException;

    /**
     * 删除临时文件
     *
     * @param file 临时文件
     */
    private void deleteTempFile(File file) {
        if (file == null) {
            return;
        }
        boolean deleted = file.delete();
        if (!deleted) {
            log.error("delete file fail");
        }
    }
}